package com.groupbuying.payproducer.service.impl;


import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.shade.com.alibaba.fastjson.JSON;
import com.groupbuying.payproducer.aliPay.AliPay;
import com.groupbuying.payproducer.aliPay.AliPayNativeConstants;
import com.groupbuying.payproducer.connector.*;
import com.groupbuying.payproducer.constants.CommonConstants;
import com.groupbuying.payproducer.constants.PayInfoEnumConstants;
import com.groupbuying.payproducer.constants.PayInfoSearchConstants;
import com.groupbuying.payproducer.context.FilterContextHandler;
import com.groupbuying.payproducer.dao.PayInfoDao;
import com.groupbuying.payproducer.domain.PayInfoDO;
import com.groupbuying.payproducer.domain.PayOrderRelDO;
import com.groupbuying.payproducer.dto.PayInfoDTO;
import com.groupbuying.payproducer.mq.PayMQConfig;
import com.groupbuying.payproducer.payCenterStrategy.PayContext;
import com.groupbuying.payproducer.service.PayOrderRelService;
import com.groupbuying.payproducer.service.PayOrderService;
import com.groupbuying.payproducer.utils.BeanOrMapConvertUtils;
import com.groupbuying.payproducer.utils.DateUtils;
import com.groupbuying.payproducer.utils.NumberValidator;
import com.groupbuying.payproducer.utils.apiresult.ApiResult;
import com.groupbuying.payproducer.wxpay.WXPay;
import com.groupbuying.payproducer.wxpay.WXPayMsgConfig;
import com.groupbuying.payproducer.wxpay.WXPayUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


@Service
public class PayOrderServiceImpl implements PayOrderService {
    public static final Logger LOGGER = LoggerFactory.getLogger(PayOrderServiceImpl.class);
    @Autowired
    private PayInfoDao payInfoDao;

    @Autowired
    private SysconfigFeign sysconfigFeign;

    @Autowired
    private PayOrderRelService payOrderRelService;

    @Autowired
    private CustomerFeign customerFeign;

    @Autowired
    private YsPayFeign ysPayFeign;

    @Autowired
    private Producer producer;

    @Autowired
    private StoreFeign storeFeign;

    @Autowired
    private FinancialFeign financialFeign;

    @Autowired
    private WechatAliFeign wechatAliFeign;

    /**
     * 统一创建支付信息
     *
     * @param payInfoDTO
     * @return
     */
    @Override
    public ApiResult createPayInfo(PayInfoDTO payInfoDTO) throws IllegalAccessException {
        /**
         * 第一步：验证参数正确（尤其是金额）
         */

        //如果支付类型是积分支付
        if (payInfoDTO.getPayType().equals(PayInfoEnumConstants.ENUM_PAYTYPE_CUR)) {
            //确保积分金额必须存在且不为空金额要求精度
            if (!NumberValidator.isNumber(payInfoDTO.getPayCurrency())) {
                return ApiResult.failure();
            }
        } else {
            //确保现金金额存在且符合金额要求精度
            if (!NumberValidator.isNumber(payInfoDTO.getPayCash())) {
                return ApiResult.failure();
            }
        }

        /**
         * 第二步：确认金额正确性（从订单中心确认）
         */
        //TODO 订单中心验证金额

        /**
         * 第三步：调用对应的支付接口，返回与支付信息
         */

        //获取当前支付渠道
        String payChannel = sysconfigFeign.getPayChannel();
        //将对象转换为Map（去除无属性的内容），并用渠道和支付类型作为策略类型调用对应支付方法。

        ApiResult result = PayContext.unifiedPayment(BeanOrMapConvertUtils.objectToMapWithoutBlank(payInfoDTO), payChannel + payInfoDTO.getPayType());
        return result;
    }

    /**
     * 积分支付并返回预支付信息
     *
     * @param map
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult createCurrencyPayInfo(Map<String, Object> map) {
        try {

            // 解析map参数
            LOGGER.info("======进入积分支付=====");
            Object tempParam = map.get("payCurrency");
            String payCurrency = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("orderId");
            String orderId = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("code");
            String code = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("formId");
            String formId = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("counterId");
            String counterId = tempParam == null ? null : tempParam.toString();

            //判断请求参数不为空
            if (StringUtils.isEmpty(orderId)) {
                LOGGER.error("订单号为空");
                return ApiResult.paramIsNull();
            }
            //TODO 还要验证金额的计算结果正确性
            if (StringUtils.isEmpty(payCurrency) || !NumberValidator.isNumber(payCurrency)) {
                LOGGER.error("支付金额不符合数字金额要求");
                return ApiResult.paramIsNull();
            }
            //通过Token获取用户ID
            String userId = FilterContextHandler.getUserID();

            /**
             * 获取收银台店铺的配置信息
             */
            String payChannel = "";
            String subject = "";
            LOGGER.info("接收的收银台ID:{}", counterId);
            //如果没有传递收银台ID，直接使用系统配置支付渠道
            if (!StringUtils.isEmpty(counterId)) {
                //查询收银台对应的商铺配置信息
                Map<String, Object> storePayInfo = storeFeign.getStorePayInfo(counterId);
                //如果没有查询到信息则按照系统支付渠道配置
                if (storePayInfo != null) {
                    payChannel = storePayInfo.get("serviceId").toString();
                    subject = storePayInfo.get("storeName").toString();
                }
            }
            LOGGER.info("商户支付渠道：{},店铺名称:{}", payChannel, subject);

            LOGGER.info("=========积分支付创建本地预支付单开始==========订单号:{}", orderId);
            //生成预支付信息
            PayInfoDO payInfoDO = new PayInfoDO();
            payInfoDO.setId(sysconfigFeign.getPayID());
            payInfoDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
            payInfoDO.setGmtCreate(new Date());
            payInfoDO.setGmtModified(new Date());
            payInfoDO.setPayState(PayInfoEnumConstants.ENUM_PAYSTATE_UNPAY);
            payInfoDO.setPayType(PayInfoEnumConstants.ENUM_PAYTYPE_CUR);
            payInfoDO.setPayChannel(PayInfoEnumConstants.ENUM_PAYCHANNEL_SELF);
            payInfoDO.setPayMoney(new BigDecimal("0"));
            payInfoDO.setPayBalance(new BigDecimal("0"));
            payInfoDO.setPayCur(new BigDecimal(payCurrency));
            payInfoDao.save(payInfoDO);

            //生成订单关联
            PayOrderRelDO payOrderRelDO = new PayOrderRelDO();
            payOrderRelDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
            payOrderRelDO.setOrderId(orderId);
            payOrderRelDO.setId(payInfoDO.getId());

            //积分无特殊要求所以对外单号就是本地支付单号
            payOrderRelDO.setOutTradeNo(payInfoDO.getId());
            payOrderRelDO.setFormId(formId);
            payOrderRelDO.setGmtCreate(new Date());
            payOrderRelDO.setGmtModified(new Date());
            payOrderRelService.save(payOrderRelDO);
            LOGGER.info("积分支付预支付关联信息保存完毕====订单号:{},本地支付单号:{}，第三方支付单号：{}", orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());

            //组装数据并返回与支付信息
            Map<String, Object> curPayInfo = new HashMap<>();
            //余额交易金额
            curPayInfo.put("payMoney", payInfoDO.getPayCur());
            //必须传递第三方支付专用的交易单号
            curPayInfo.put("orderNum", payOrderRelDO.getOutTradeNo());
            if (StringUtils.isEmpty(subject)) {
                subject = "订单号" + payOrderRelDO.getOrderId();
            } else {
                subject = subject + ",订单号" + payOrderRelDO.getOrderId();
            }
            //商品描述
            curPayInfo.put("subject", subject);
            //用户标识
            curPayInfo.put("customerUuid", userId);
            //TODO 异步回调地址
            curPayInfo.put("notifyUrl", "");
            curPayInfo.put("Authorization", FilterContextHandler.getToken());
            //发起积分支付
            //TODO  执行HTTP请求
            Map<String, Object> res = financialFeign.integralprepay(curPayInfo);
            LOGGER.info("调用积分支付回执结果:{},===========订单号:{},本地支付单号:{}，第三方支付单号：{}", res, orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());
            //获取预支付回执、支付调起信息
            if (res == null || !"1".equals(res.get("code").toString()) || res.get("result") == null) {
                LOGGER.error("调用积分预支付信息出错,支付单号:{},订单号:{}", payInfoDO.getId(), payOrderRelDO.getOrderId());
                return ApiResult.failure("预支付请求错误");
            }
            Map<String, String> info = (Map<String, String>) JSON.parse(res.get("result").toString());
            Map<String, String> jsapi_pay_info = new HashMap<>();
//            jsapi_pay_info.put("prePayCode", info.get("prePayCode"));
            jsapi_pay_info.put("prePayNo", info.get("prepayNo"));
            jsapi_pay_info.put("payMoney", info.get("payCurrency"));
            jsapi_pay_info.put("payId", payInfoDO.getId());
            //设置定时检测支付结果的MQ
            payStateCheck(PayMQConfig.TAG_CUR_TIMING, payOrderRelDO.getOutTradeNo());
            return ApiResult.success(jsapi_pay_info);
        } catch (Exception e) {
            e.printStackTrace();
            return ApiResult.failure("预支付请求错误");
        }

    }

    /**
     * 创建余额支付信息
     *
     * @param map
     * @return com.groupbuying.payproducer.utils.apiresult.ApiResult
     * @Author Vincentxin
     * @Date 2019-08-26 10:44
     **/
    @Override
    public ApiResult createBalancePayInfo(Map<String, Object> map) {
        try {
            // 解析map参数
            LOGGER.info("======进入余额支付=====");
            Object tempParam = map.get("payBalance");
            String payBalance = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("orderId");
            String orderId = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("code");
            String code = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("formId");
            String formId = tempParam == null ? null : tempParam.toString();
            tempParam = map.get("counterId");
            String counterId = tempParam == null ? null : tempParam.toString();
            //判断请求参数不为空
            if (StringUtils.isEmpty(orderId)) {
                LOGGER.error("订单号为空");
                return ApiResult.paramIsNull();
            }
            if (StringUtils.isEmpty(payBalance) || !NumberValidator.isNumber(payBalance)) {
                LOGGER.error("支付金额不符合数字金额要求");
                return ApiResult.paramIsNull();
            }
            /**
             * TODO 获取用户CustomerUUID
             */
            //通过Token获取用户ID
            String userId = FilterContextHandler.getUserID();
            //通过用户ID获取微信够样商户UUID

            String customerUuid = customerFeign.getMainUuidByCustomerId(userId);
            if (StringUtils.isEmpty(customerUuid)) {
                //通过code获取用户的openID
                LOGGER.error("获取够样商户UUID失败");
//            customerUuid = "959ce01d5cbc4c41af113b4b8f92d274";
            }

            /**
             * 获取收银台店铺的配置信息
             */
            String payChannel = "";
            String subject = "";
            LOGGER.info("接收的收银台ID:{}", counterId);
            //如果没有传递收银台ID，直接使用系统配置支付渠道
            if (!StringUtils.isEmpty(counterId)) {
                //查询收银台对应的商铺配置信息
                Map<String, Object> storePayInfo = storeFeign.getStorePayInfo(counterId);
                //如果没有查询到信息则按照系统支付渠道配置
                if (storePayInfo != null) {
                    payChannel = storePayInfo.get("serviceId").toString();
                    subject = storePayInfo.get("storeName").toString();
                }
            }
            LOGGER.info("支付渠道渠道：{},店铺名称:{}", payChannel, counterId);

            LOGGER.info("=========余额支付==========订单号:{}", orderId);
            PayInfoDO payInfoDO = new PayInfoDO();
            payInfoDO.setId(sysconfigFeign.getPayID());
            payInfoDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
            payInfoDO.setGmtCreate(new Date());
            payInfoDO.setGmtModified(new Date());
            payInfoDO.setPayState(PayInfoEnumConstants.ENUM_PAYSTATE_UNPAY);
            payInfoDO.setPayType(PayInfoEnumConstants.ENUM_PAYTYPE_BAL);
            payInfoDO.setPayChannel(PayInfoEnumConstants.ENUM_PAYCHANNEL_SELF);
            payInfoDO.setPayMoney(new BigDecimal(0));
            payInfoDO.setPayBalance(new BigDecimal(payBalance));
            payInfoDO.setPayCur(new BigDecimal(0));
            payInfoDao.save(payInfoDO);
            LOGGER.info("余额支付保存预支付信息完毕====订单号:{},本地支付单号:{}", orderId, payInfoDO.getId());
            //生成订单关联
            PayOrderRelDO payOrderRelDO = new PayOrderRelDO();
            payOrderRelDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
            payOrderRelDO.setOrderId(orderId);
            payOrderRelDO.setId(payInfoDO.getId());
            //余额中心没有单独要求交易单号设置
            payOrderRelDO.setOutTradeNo(payInfoDO.getId());
            //用于微信通知的formID
            payOrderRelDO.setFormId(formId);
            payOrderRelDO.setGmtCreate(new Date());
            payOrderRelDO.setGmtModified(new Date());
            payOrderRelService.save(payOrderRelDO);
            LOGGER.info("余额支付预支付关联信息保存完毕====订单号:{},本地支付单号:{}，第三方支付单号：{}", orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());
            //组装数据并返回与支付信息
            Map<String, Object> balPayInfo = new HashMap<>();
            //余额交易金额
            balPayInfo.put("payMoney", payInfoDO.getPayBalance());
            //必须传递第三方支付专用的交易单号
            balPayInfo.put("orderNum", payOrderRelDO.getOutTradeNo());
            if (StringUtils.isEmpty(subject)) {
                subject = "订单号" + payOrderRelDO.getOrderId();
            } else {
                subject = subject + ",订单号" + payOrderRelDO.getOrderId();
            }
            //商品描述
            balPayInfo.put("subject", subject);
            //用户标识
            balPayInfo.put("customerUuid", customerUuid);
            //TODO 异步回调地址
            balPayInfo.put("notifyUrl", "");


            //发起余额支付
            //TODO  执行HTTP请求
            Map<String, Object> res = financialFeign.balancePay(balPayInfo);
            LOGGER.info("调用余额支付回执结果:{},===========订单号:{},本地支付单号:{}，第三方支付单号：{}", res, orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());
            //获取支付调起信息
            if (res == null || !"1".equals(res.get("code").toString()) || res.get("result") == null) {
                LOGGER.error("调用余额预支付信息出错,支付单号:{},订单号:{}", payInfoDO.getId(), payOrderRelDO.getOrderId());
                return ApiResult.failure("预支付请求错误");
            }
            Map<String, String> info = (Map<String, String>) JSON.parse(res.get("result").toString());
            Map<String, String> jsapi_pay_info = new HashMap<>();
            jsapi_pay_info.put("prePayCode", info.get("prePayCode"));
            jsapi_pay_info.put("prePayNo", info.get("prePayNo"));
            jsapi_pay_info.put("payMoney", info.get("payMoney"));
            jsapi_pay_info.put("payId", payInfoDO.getId());
            //设置定时检测支付结果的MQ
            payStateCheck(PayMQConfig.TAG_BALANCE_TIMING, payOrderRelDO.getOutTradeNo());
            return ApiResult.success(jsapi_pay_info);
        } catch (Exception e) {
            e.printStackTrace();
            return ApiResult.failure("预支付请求错误");
        }
    }

    /**
     * 微信支付下单并返回与支付信息
     *
     * @param mapParams
     * @return
     */
    @Override
    @Transactional
    public ApiResult createWXPayInfo(Map<String, Object> mapParams) {
        try {
            // 解析map参数
            Object tempParam = mapParams.get("payMoney");
            String payMoney = tempParam == null ? null : tempParam.toString();
            tempParam = mapParams.get("orderId");
            String orderId = tempParam == null ? null : tempParam.toString();
            tempParam = mapParams.get("code");
            String code = tempParam == null ? null : tempParam.toString();
            tempParam = mapParams.get("formId");
            String formId = tempParam == null ? null : tempParam.toString();
            tempParam = mapParams.get("counterId");
            String counterId = tempParam == null ? null : tempParam.toString();
            //判断请求参数不为空
            if (StringUtils.isEmpty(formId) || StringUtils.isEmpty(orderId) || StringUtils.isEmpty(payMoney) || StringUtils.isEmpty(code) || !NumberValidator.isNumber(payMoney)) {
                return ApiResult.paramIsNull();
            }
            /**
             * 获取用户openID
             */
            //通过Token获取用户ID
            String userId = FilterContextHandler.getUserID();
            //通过用户ID获取微信openID
            String openid = customerFeign.getOpenidByCustomerId(userId);
            if (StringUtils.isEmpty(openid)) {
                //通过code获取用户的openID
                Map<String,String> map = wechatAliFeign.getOpenidAndSessionKeyByCode(code,"youdianproductor");
                if (map == null || StringUtils.isEmpty(map.get("openid"))){
                    LOGGER.info("微信小程序获取openid为空");
                    return ApiResult.paramIsNull();
                }
            }
            /**
             * 获取收银台店铺的配置信息
             */
            String payChannel = "";
            String subject = "";
            LOGGER.info("接收的收银台ID:{}", counterId);
            //如果没有传递收银台ID，直接使用系统配置支付渠道
            if (StringUtils.isEmpty(counterId)) {
                //通过订单ID查询收银台ID，获取店铺信息
                payChannel = sysconfigFeign.getPayChannel();
            } else {
                //查询收银台对应的商铺配置信息
                Map<String, Object> storePayInfo = storeFeign.getStorePayInfo(counterId);
                //如果没有查询到信息则按照系统支付渠道配置
                if (storePayInfo == null || storePayInfo.size() == 0) {
                    LOGGER.error("支付中店铺支付信息配置信息获取失败，counterId：{}", counterId);
                    payChannel = sysconfigFeign.getPayChannel();
                } else {
                    payChannel = storePayInfo.get("serviceId").toString();
                    subject = storePayInfo.get("storeName").toString();
                }
            }
            LOGGER.info("支付渠道渠道：{},店铺名称:{}", payChannel, counterId);
            //判断支付渠道
            if (PayInfoEnumConstants.ENUM_PAYCHANNEL_OR.equals(payChannel)) {
                //原生微信支付
                //生成本地预支付信息
                LOGGER.info("=======微信原生支付开始========");
                PayInfoDO payInfoDO = new PayInfoDO();
                payInfoDO.setId(sysconfigFeign.getPayID());
                payInfoDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payInfoDO.setGmtCreate(new Date());
                payInfoDO.setGmtModified(new Date());
                payInfoDO.setPayState(PayInfoEnumConstants.ENUM_PAYSTATE_UNPAY);
                payInfoDO.setPayType(PayInfoEnumConstants.ENUM_PAYTYPE_WX);
                payInfoDO.setPayChannel(PayInfoEnumConstants.ENUM_PAYCHANNEL_OR);
                payInfoDO.setPayMoney(new BigDecimal(payMoney));
                payInfoDO.setPayBalance(new BigDecimal(0));
                payInfoDO.setPayCur(new BigDecimal(0));
                payInfoDao.save(payInfoDO);

                //生成订单关联
                PayOrderRelDO payOrderRelDO = new PayOrderRelDO();
                payOrderRelDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payOrderRelDO.setOrderId(orderId);
                payOrderRelDO.setId(payInfoDO.getId());
                //微信无特殊要求所以对外单号就是本地支付单号
                payOrderRelDO.setOutTradeNo(payInfoDO.getId());
                //用于微信通知的formID
                payOrderRelDO.setFormId(formId);
                payOrderRelDO.setGmtCreate(new Date());
                payOrderRelDO.setGmtModified(new Date());
                payOrderRelService.save(payOrderRelDO);

                //生成微信预支付回执
                Map wxMap = new HashMap();
                wxMap.put(PayInfoSearchConstants.SEARCH_ID, payInfoDO.getId());
                wxMap.put("open_id", openid);
                if (StringUtils.isEmpty(subject)) {
                    subject = "订单号" + payOrderRelDO.getOrderId();
                } else {
                    subject = subject + ",订单号" + payOrderRelDO.getOrderId();
                }
                wxMap.put("body", subject);
                wxMap.put(PayInfoSearchConstants.SEARCH_PAYMONEY, payMoney);
                //设置支付情况的定时检查MQ
                payStateCheck(PayMQConfig.TAG_WX_PAYBACK, payOrderRelDO.getOutTradeNo());
                return this.weiXinPrePaymentMsg(wxMap);
            } else if (PayInfoEnumConstants.ENUM_PAYCHANNEL_YS.equals(payChannel)) {
                //银盛支付通道
                LOGGER.info("=======银盛微信支付==========订单号:{}", orderId);
                PayInfoDO payInfoDO = new PayInfoDO();
                payInfoDO.setId(sysconfigFeign.getPayID());
                payInfoDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payInfoDO.setGmtCreate(new Date());
                payInfoDO.setGmtModified(new Date());
                payInfoDO.setPayState(PayInfoEnumConstants.ENUM_PAYSTATE_UNPAY);
                payInfoDO.setPayType(PayInfoEnumConstants.ENUM_PAYTYPE_WX);
                payInfoDO.setPayChannel(PayInfoEnumConstants.ENUM_PAYCHANNEL_YS);
                payInfoDO.setPayMoney(new BigDecimal(payMoney));
                payInfoDO.setPayBalance(new BigDecimal(0));
                payInfoDO.setPayCur(new BigDecimal(0));
                payInfoDao.save(payInfoDO);
                LOGGER.info("银盛微信支付保存预支付信息完毕====订单号:{},本地支付单号:{}", orderId, payInfoDO.getId());
                //生成订单关联
                PayOrderRelDO payOrderRelDO = new PayOrderRelDO();
                payOrderRelDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payOrderRelDO.setOrderId(orderId);
                payOrderRelDO.setId(payInfoDO.getId());
                //银盛要求支付单号前八位必须是时间, 且长度不能大于32位
                payOrderRelDO.setOutTradeNo(createYsOutTradeNo(payInfoDO.getId()));
                //用于微信通知的formID
                payOrderRelDO.setFormId(formId);
                payOrderRelDO.setGmtCreate(new Date());
                payOrderRelDO.setGmtModified(new Date());
                payOrderRelService.save(payOrderRelDO);
                LOGGER.info("银盛微信支付预支付关联信息保存完毕====订单号:{},本地支付单号:{}，第三方支付单号：{}", orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());
                //组装银盛数据并返回与支付信息
                Map<String, Object> ysPayInfo = new HashMap<>();
                ysPayInfo.put("total_amount", payInfoDO.getPayMoney());
                //必须传递第三方支付专用的交易单号
                ysPayInfo.put("out_trade_no", payOrderRelDO.getOutTradeNo());
                if (StringUtils.isEmpty(subject)) {
                    subject = "订单号" + payOrderRelDO.getOrderId();
                } else {
                    subject = subject + ",订单号" + payOrderRelDO.getOrderId();
                }
                ysPayInfo.put("subject", subject);
                ysPayInfo.put("sub_openid", openid);

                //发起银盛微信支付
                Map<String, Object> res = ysPayFeign.miniAppPay(ysPayInfo);
                //TODO 获取jsapi_pay_info 返回给前端调起支付并增加PayId 支付单信息
                LOGGER.info("调用银盛支付回执结果:{},===========订单号:{},本地支付单号:{}，第三方支付单号：{}", res, orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());
                //获取支付调起信息
                String info = res.get("jsapi_pay_info").toString();
                if (StringUtils.isEmpty(info)) {
                    return ApiResult.failure("预支付失败");
                }
                Map<String, String> jsapi_pay_info = new HashMap<>();
                jsapi_pay_info.putAll((Map<String, String>) JSON.parse(info));
                jsapi_pay_info.put("payId", payInfoDO.getId());
                //设置定时检测支付结果的MQ
                payStateCheck(PayMQConfig.TAG_YS_TIMING, payOrderRelDO.getOutTradeNo());
                return ApiResult.success(jsapi_pay_info);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return ApiResult.failure("预支付失败");
    }

    /**
     * 支付宝下单并返回预支付信息
     *
     * @param map
     * @return
     */
    @Override
    @Transactional
    public ApiResult createAliPayInfo(Map<String, String> map) {
        try {
            //交易金额
            String payMoney = map.get("payMoney");
            //订单号
            String orderId = map.get("orderId");
            //支付宝用户code
            String code = map.get("code");
            //收银台ID
            String counterId = map.get("counterId");

            if (StringUtils.isEmpty(payMoney) && !NumberValidator.isNumber(payMoney)) {
                LOGGER.error("支付金额必须符合支付金额数字要求");
                return ApiResult.failure("支付金额必须符合支付金额数字要求");
            }
            if (StringUtils.isEmpty(orderId)) {
                LOGGER.error("订单号不能为空");
                return ApiResult.failure("订单号不能为空");
            }
            /**
             * 获取用户aliPayUserID
             */
            //通过token获取用户ID
            String userID = FilterContextHandler.getUserID();
            //通过用户ID获取支付宝用户的用户ID
            String buyerId = customerFeign.getAliUserId(userID);
            //如果查询失败直接通过code获取支付宝用户ID
            if (StringUtils.isEmpty(buyerId)) {
                //调用支付宝授权登录Feign接口
                Map<String, String> maps = wechatAliFeign.aliGetUserIdAccessCodeByAuthCode(code,"youdianproductor");
                if (maps == null) {
                    return ApiResult.failure("支付宝小程序通过authCode获取alipayUserId为空");
                }
                buyerId = maps.get("alipayUserId");
            }
            if (StringUtils.isEmpty(buyerId)) {
                LOGGER.error("支付宝用户ID不能为空");
                return ApiResult.failure("支付宝用户ID不能为空");
            }

            /**
             * 获取收银台店铺的配置信息
             */
            String payChannel = "";
            String subject = "";
            LOGGER.info("接收的收银台ID:{}", counterId);
            if (StringUtils.isEmpty(counterId)) {
                //通过订单ID查询收银台ID，获取店铺信息
                payChannel = sysconfigFeign.getPayChannel();
            } else {
                Map<String, Object> storePayInfo = storeFeign.getStorePayInfo(counterId);
                if (storePayInfo == null || storePayInfo.size() == 0) {
                    LOGGER.error("支付中店铺支付信息配置信息获取失败，counterId：{}", counterId);
                    payChannel = sysconfigFeign.getPayChannel();
                } else {
                    payChannel = storePayInfo.get("serviceId").toString();
                    subject = storePayInfo.get("storeName").toString();
                }
            }
            LOGGER.info("支付渠道渠道：{},店铺名称:{}", payChannel, counterId);
            //判断当前的支付渠道是否是银盛支付
            if (PayInfoEnumConstants.ENUM_PAYCHANNEL_YS.equals(payChannel)) {
                //银盛支付通道
                LOGGER.info("进入银盛支付宝支付模块发起支付，订单ID:{},支付金额:{},支付用户:{}", orderId, payMoney, buyerId);
                PayInfoDO payInfoDO = new PayInfoDO();
                payInfoDO.setId(sysconfigFeign.getPayID());
                payInfoDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payInfoDO.setGmtCreate(new Date());
                payInfoDO.setGmtModified(new Date());
                payInfoDO.setPayState(PayInfoEnumConstants.ENUM_PAYSTATE_UNPAY);
                payInfoDO.setPayType(PayInfoEnumConstants.ENUM_PAYTYPE_ALI);
                payInfoDO.setPayChannel(PayInfoEnumConstants.ENUM_PAYCHANNEL_YS);
                payInfoDO.setPayMoney(new BigDecimal(payMoney));
                payInfoDO.setPayBalance(new BigDecimal(0));
                payInfoDO.setPayCur(new BigDecimal(0));
                payInfoDao.save(payInfoDO);
                LOGGER.info("支付信息保存成功 支付单号：{}", payInfoDO.getId());
                //生成订单关联
                PayOrderRelDO payOrderRelDO = new PayOrderRelDO();
                payOrderRelDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payOrderRelDO.setOrderId(orderId);
                payOrderRelDO.setId(payInfoDO.getId());
                //银盛要求支付单号前八位必须是时间, 且长度不能大于32位
                payOrderRelDO.setOutTradeNo(createYsOutTradeNo(payInfoDO.getId()));
                //TODO 用于微信通知的formID支付宝支付该如何处理
                payOrderRelDO.setFormId(map.get("formId"));
                payOrderRelDO.setGmtCreate(new Date());
                payOrderRelDO.setGmtModified(new Date());
                payOrderRelService.save(payOrderRelDO);
                LOGGER.info("支付关联订单及第三方单号保存成功，支付单号:{},订单号：{}，第三方支付单号：{}", orderId, payMoney, payOrderRelDO.getOutTradeNo());

                //组装银盛数据并返回与支付信息
                Map<String, Object> ysPayInfo = new HashMap<>();
                ysPayInfo.put("total_amount", payInfoDO.getPayMoney());
                //必须传递第三方支付专用的交易单号
                ysPayInfo.put("out_trade_no", payOrderRelDO.getOutTradeNo());
                if (StringUtils.isEmpty(subject)) {
                    subject = "订单号" + payOrderRelDO.getOrderId();
                } else {
                    subject = subject + ",订单号" + payOrderRelDO.getOrderId();
                }
                ysPayInfo.put("subject", subject);
                ysPayInfo.put("buyer_id", buyerId);
                //发起银盛支付宝支付
                LOGGER.info("=========start 开始请求银盛支付宝支付=====");
                Map<String, Object> res = ysPayFeign.aliPay(ysPayInfo);
                LOGGER.info("请求响应结果：{}", res);
                //获取支付调起信息
                if (res.get("jsapi_pay_info") == null) {
                    LOGGER.error("银盛支付宝支付请求失败，订单号{}，支付单号{}，第三方支付单号{}", orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());
                    return ApiResult.failure("银盛支付宝支付请求失败");
                }
                String info = res.get("jsapi_pay_info").toString();
                if (StringUtils.isEmpty(info)) {
                    return ApiResult.failure("支付失败");
                }
                LOGGER.info("银盛支付宝支付响应支付调起信息{}", info);
                Map<String, String> jsapi_pay_info = new HashMap<>();
                jsapi_pay_info.putAll((Map<String, String>) JSON.parse(info));
                jsapi_pay_info.put("payId", payInfoDO.getId());
                //支付结果定时检测
                payStateCheck(PayMQConfig.TAG_YS_TIMING, payOrderRelDO.getOutTradeNo());
                LOGGER.info("银盛支付支付回传完成{}", jsapi_pay_info);
                return ApiResult.success(jsapi_pay_info);
                //当前支付渠道是原生支付宝支付
            } else if (PayInfoEnumConstants.ENUM_PAYCHANNEL_OR.equals(payChannel)) {
                LOGGER.info("进入原生支付宝支付模块发起支付，订单ID:{},支付金额:{},支付用户:{}", orderId, payMoney, buyerId);
                PayInfoDO payInfoDO = new PayInfoDO();
                payInfoDO.setId(sysconfigFeign.getPayID());
                payInfoDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payInfoDO.setGmtCreate(new Date());
                payInfoDO.setGmtModified(new Date());
                payInfoDO.setPayState(PayInfoEnumConstants.ENUM_PAYSTATE_UNPAY);
                payInfoDO.setPayType(PayInfoEnumConstants.ENUM_PAYTYPE_ALI);
                payInfoDO.setPayChannel(PayInfoEnumConstants.ENUM_PAYCHANNEL_OR);
                payInfoDO.setPayMoney(new BigDecimal(payMoney));
                payInfoDO.setPayBalance(new BigDecimal(0));
                payInfoDO.setPayCur(new BigDecimal(0));
                payInfoDao.save(payInfoDO);
                LOGGER.info("支付信息保存成功 支付单号：{}", payInfoDO.getId());
                //生成订单关联
                PayOrderRelDO payOrderRelDO = new PayOrderRelDO();
                payOrderRelDO.setIsDelete(CommonConstants.COMMON_FLAG_UNDEL);
                payOrderRelDO.setOrderId(orderId);
                payOrderRelDO.setId(payInfoDO.getId());
                //原生支付宝对outTradeNo没有没要求，所以直接使用支付单号作为第三方单号
                payOrderRelDO.setOutTradeNo(payInfoDO.getId());
                //TODO 用于微信通知的formID支付宝支付该如何处理
                payOrderRelDO.setFormId(map.get("formId"));
                payOrderRelDO.setGmtCreate(new Date());
                payOrderRelDO.setGmtModified(new Date());
                payOrderRelService.save(payOrderRelDO);
                LOGGER.info("支付关联订单及第三方单号保存成功，支付单号:{},订单号：{}，第三方支付单号：{}");

                //组装原生支付宝支付参数并返回与支付信息
                Map<String, String> aliPayInfo = new HashMap<>();
                aliPayInfo.put("total_amount", payInfoDO.getPayMoney().toString());
                //必须传递第三方支付专用的交易单号
                aliPayInfo.put("out_trade_no", payOrderRelDO.getOutTradeNo());
                if (StringUtils.isEmpty(subject)) {
                    subject = "订单号" + payOrderRelDO.getOrderId();
                } else {
                    subject = subject + ",订单号" + payOrderRelDO.getOrderId();
                }
                aliPayInfo.put("subject", subject);
                aliPayInfo.put("buyer_id", buyerId);
                //发起原生支付宝支付
                LOGGER.info("=========start 开始请求原生支付宝支付=====");
                Map<String, String> initParam = sysconfigFeign.getByAppliName(AliPayNativeConstants.APPLICATION_NAME);
                String tradeNO = AliPay.aliPayCreate(aliPayInfo, initParam);
                LOGGER.info("请求响应结果：{}", tradeNO);
                //获取支付调起信息
                if (tradeNO == null) {
                    LOGGER.error("原生支付宝支付请求失败，订单号{}，支付单号{}，第三方支付单号{}", orderId, payInfoDO.getId(), payOrderRelDO.getOutTradeNo());
                    return ApiResult.failure("原生支付宝支付请求失败");
                }
                Map<String, String> jsapi_pay_info = new HashMap<>();
                jsapi_pay_info.put("tradeNO", tradeNO);
                jsapi_pay_info.put("payId", payInfoDO.getId());
                //支付结果定时检测
                payStateCheck(PayMQConfig.TAG_ALI_TIMING, payOrderRelDO.getOutTradeNo());
                LOGGER.info("原生支付宝预支付信息回传完成{}", jsapi_pay_info);
                return ApiResult.success(jsapi_pay_info);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ApiResult.failure("支付宝支付请求失败");
    }


    /**
     * 原生微信支付工具方法
     *
     * @param paramsMap
     * @return
     */
    public ApiResult weiXinPrePaymentMsg(Map<String, String> paramsMap) {
        Map<String, String> data = new HashMap<String, String>();
        //TODO 需要修改支付单提示为商户店铺信息加订单信息
        data.put("body", paramsMap.get("body"));
        data.put("out_trade_no", paramsMap.get(PayInfoSearchConstants.SEARCH_ID));
        data.put("openid", paramsMap.get("open_id"));
        data.put("device_info", "");
        data.put("fee_type", "CNY");
        //金额必须是分
        BigDecimal money = new BigDecimal(paramsMap.get(PayInfoSearchConstants.SEARCH_PAYMONEY)).multiply(new BigDecimal("100"));

        data.put("total_fee", new DecimalFormat("#").format(money));
        //此处指定为扫码支付
        data.put("trade_type", "JSAPI");
        data.put("product_id", "12");
        try {
            Map<String, String> resp = WXPay.unifiedOrder(data);

            //为前端支付加密
            Map<String, String> respSecret = new HashMap<>();
            respSecret.put("appId", WXPayMsgConfig.SMALL_APP_ID);
            respSecret.put("timeStamp", String.valueOf((new Date()).getTime()));
            respSecret.put("nonceStr", resp.get("nonce_str"));
            respSecret.put("package", "prepay_id=" + resp.get("prepay_id"));
            respSecret.put("signType", WXPayMsgConfig.SIGN_TYPE);
            respSecret.put("paySign", WXPayUtil.generateSignature(respSecret, WXPayMsgConfig.KEY));
            respSecret.put("payId", paramsMap.get(PayInfoSearchConstants.SEARCH_ID));
            System.out.println(respSecret);
            return ApiResult.success(respSecret);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ApiResult.failure();
    }


    /**
     * 生成符合银盛要求的支付单号
     *
     * @param payId 本地支付单信息
     * @return
     */
    public String createYsOutTradeNo(String payId) {
        String prefix = DateUtils.format(new Date(), DateUtils.YMD2);
        if (payId.length() >= 24) {
            String substr = payId.substring(payId.length() - 24, payId.length() - 1);
            return prefix + substr;
        }
        return prefix + payId;
    }

    /**
     * 发送定时检查mq 同步检查支付情况
     *
     * @param dealNo
     */
    private void payStateCheck(String tag, String dealNo) {
        try {
            Message message = new Message(PayMQConfig.TOPIC, tag, ("{\"key\":\"" + dealNo + "\"}").getBytes());
            //默认时间是十秒钟之后发起支付结果查询
            message.setStartDeliverTime(System.currentTimeMillis() + 20000);
            producer.send(message);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}
